package tech.jitao.aly_oss;

import com.alibaba.sdk.android.oss.ClientConfiguration;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.OSS;
import com.alibaba.sdk.android.oss.OSSClient;
import com.alibaba.sdk.android.oss.ServiceException;
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback;
import com.alibaba.sdk.android.oss.common.OSSConstants;
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSFederationCredentialProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSFederationToken;
import com.alibaba.sdk.android.oss.common.utils.IOUtils;
import com.alibaba.sdk.android.oss.model.DeleteObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectResult;
import com.google.common.collect.Maps;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import org.json.JSONObject;

import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.net.MalformedURLException;
import io.flutter.Log;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

public class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
    private final MethodChannel channel;
    private final PluginRegistry.Registrar registrar;
    private OSS oss;
    private String bucketName;
    private String bucketDir;

    MethodCallHandlerImpl(MethodChannel channel, PluginRegistry.Registrar registrar) {
        this.channel = channel;
        this.registrar = registrar;
    }

    @Override
    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
        switch (methodCall.method) {
            case "init":
                init(methodCall, result);
                break;
            case "upload":
                upload(methodCall, result);
                break;
            case "exist":
                exist(methodCall, result);
                break;
            case "delete":
                delete(methodCall, result);
                break;
            default:
                result.notImplemented();
        }
    }

    private void init(MethodCall call, MethodChannel.Result result) {
        final String instanceId = call.argument("instanceId");
        final String requestId = call.argument("requestId");
        final String stsServer = call.argument("stsServer");
        final String endpoint = call.argument("endpoint");
        final String aesKey = call.argument("aesKey");
        final String token = call.argument("token");
        final String iv = call.argument("iv");
        final OSSCredentialProvider credentialProvider = new OSSFederationCredentialProvider() {
            @Override
            public OSSFederationToken getFederationToken() {
                try {
                    URL url = new URL(stsServer);
                    JSONObject requestObject = new JSONObject();
                    requestObject.put("ConcoctBucketName",bucketName);
                    requestObject.put("ConcoctDir",bucketDir);
                    String requestData = requestObject.toString();
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestProperty("access-token",token);
                    conn.setRequestProperty("Content-Type","application/json");
                    conn.setRequestMethod("POST");
                    conn.setDoOutput(true);
                    OutputStream out = conn.getOutputStream();
                    out.write(requestData.getBytes());
                    out.flush();
                    out.close();
                    InputStream input = conn.getInputStream();
                    String data = IOUtils.readStreamAsString(input, OSSConstants.DEFAULT_CHARSET_NAME);
                    JSONObject resObj = new JSONObject(data);
                    String jsonText = AesHelper.decrypt(aesKey, iv, resObj.getString("Data"));
                    JSONObject jsonObj = new JSONObject(jsonText);

                    return new OSSFederationToken(jsonObj.getString("AccessKeyId"),
                            jsonObj.getString("AccessKeySecret"),
                            jsonObj.getString("SecurityToken"),
                            jsonObj.getString("Expiration"));
                } catch (Exception e) {
                    Log.w("OSSFederationCredentialProvider", e.getMessage());
                }
                return null;
            }
        };

        final ClientConfiguration conf = new ClientConfiguration();
        conf.setConnectionTimeout(15 * 1000); // 连接超时时间，默认15秒
        conf.setSocketTimeout(15 * 1000); // Socket超时时间，默认15秒
        conf.setMaxConcurrentRequest(5); // 最大并发请求数，默认5个
        conf.setMaxErrorRetry(2); // 失败后最大重试次数，默认2次

        oss = new OSSClient(registrar.context(), endpoint, credentialProvider, conf);

        final Map<String, String> arguments = Maps.newHashMap();
        arguments.put("instanceId", instanceId);
        arguments.put("requestId", requestId);

        result.success(arguments);
    }

    private void upload(MethodCall call, MethodChannel.Result result) {
        if (!checkOss(result)) {
            return;
        }
        final  MethodChannel.Result res =  result;
        final String instanceId = call.argument("instanceId");
        final String requestId = call.argument("requestId");
        bucketName = call.argument("bucket");
        bucketDir = call.argument("bucketDir");
        final String key = call.argument("key");
        final String file = call.argument("file");
        CompressUtil.compressFile(file, new CompressUtil.CompressCallback() {
            @Override
            public void onCompress(File file) {
                //这个就是绝对路径传进去就好
                final String filePath = file.getAbsolutePath();
                //在这里调用上传
                PutObjectRequest request = new PutObjectRequest(bucketName,bucketDir + "/" + key, filePath);
                request.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
                    @Override
                    public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                        final Map<String, String> arguments = Maps.newHashMap();
                        arguments.put("instanceId", instanceId);
                        arguments.put("requestId", requestId);
                        arguments.put("bucket", bucketName);
                        arguments.put("key", key);
                        arguments.put("currentSize", String.valueOf(currentSize));
                        arguments.put("totalSize", String.valueOf(totalSize));
                        invokeMethod("onProgress", arguments);
                    }
                });

                oss.asyncPutObject(request, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {

                            @Override
                            public void onSuccess(PutObjectRequest request, PutObjectResult result) {
                                System.out.println("onSuccess");
                                BitmapFactory.Options options = new BitmapFactory.Options();
                                options.inJustDecodeBounds = true;
                                BitmapFactory.decodeFile(filePath, options);
                                //下面是图片的宽高
                                int width = options.outWidth;
                                int height = options.outHeight;

                                final Map<String, String> arguments = Maps.newHashMap();
                                arguments.put("success", "true");
                                arguments.put("instanceId", instanceId);
                                arguments.put("requestId", requestId);
                                arguments.put("bucket", bucketName);
                                arguments.put("key", key);
                                arguments.put("width", Integer.toString(width));
                                arguments.put("height", Integer.toString(height));
                                invokeMethod("onUpload", arguments);
                                System.out.println("onSuccess2222");
                            }

                            @Override
                            public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
                                System.out.println("onFailure");
                                final Map<String, String> arguments = Maps.newHashMap();
                                arguments.put("success", "false");
                                arguments.put("instanceId", instanceId);
                                arguments.put("requestId", requestId);
                                arguments.put("bucket", bucketName);
                                arguments.put("key", key);

                                if (clientException != null) {
                                    Log.w("onFailure", "ClientException: " + clientException.getMessage());

                                    arguments.put("message", clientException.getMessage());
                                }

                                if (serviceException != null) {
                                    Log.w("onFailure",
                                            "ServiceException: ErrorCode=" + serviceException.getErrorCode() +
                                                    "RequestId" + serviceException.getRequestId() +
                                                    "HostId" + serviceException.getHostId() +
                                                    "RawMessage" + serviceException.getRawMessage());

                                    arguments.put("message", serviceException.getRawMessage());
                                }

                                invokeMethod("onUpload", arguments);
                            }
                        }
                );

                final Map<String, String> map = Maps.newHashMap();
                map.put("instanceId", instanceId);
                map.put("requestId", requestId);
                map.put("bucket", bucketName);
                map.put("key", key);

                res.success(map);
            }
        },registrar.context().getCacheDir().getPath());

    }

    private void exist(MethodCall call, MethodChannel.Result result) {
        if (!checkOss(result)) {
            return;
        }

        final String instanceId = call.argument("instanceId");
        final String requestId = call.argument("requestId");
        final String bucket = call.argument("bucket");
        final String key = call.argument("key");

        try {
            final Map<String, String> map = Maps.newHashMap();
            map.put("instanceId", instanceId);
            map.put("requestId", requestId);
            map.put("bucket", bucket);
            map.put("key", key);

            if (oss.doesObjectExist(bucket, key)) {
                map.put("exist", "true");
            } else {
                map.put("exist", "false");
            }

            result.success(map);
        } catch (ClientException e) {
            Log.w("doesObjectExist", "ClientException: " + e.getMessage());

            result.error(ErrorCodes.CLIENT_EXCEPTION, e.getMessage(), null);
        } catch (ServiceException e) {
            Log.w("doesObjectExist", "ServiceException: " + e.getRawMessage());

            result.error(ErrorCodes.SERVICE_EXCEPTION, e.getMessage(), e.getRawMessage());
        }
    }

    private void delete(MethodCall call, MethodChannel.Result result) {
        if (!checkOss(result)) {
            return;
        }

        final String instanceId = call.argument("instanceId");
        final String requestId = call.argument("requestId");
        final String bucket = call.argument("bucket");
        final String key = call.argument("key");


        DeleteObjectRequest request = new DeleteObjectRequest(bucket, key);

        try {
            oss.deleteObject(request);
            final Map<String, String> map = Maps.newHashMap();
            map.put("instanceId", instanceId);
            map.put("requestId", requestId);
            map.put("bucket", bucket);
            map.put("key", key);

            result.success(map);
        } catch (ClientException e) {
            Log.w("deleteObject", "ClientException: " + e.getMessage());

            result.error(ErrorCodes.CLIENT_EXCEPTION, e.getMessage(), null);
        } catch (ServiceException e) {
            Log.w("deleteObject", "ServiceException: " + e.getRawMessage());

            result.error(ErrorCodes.SERVICE_EXCEPTION, e.getMessage(), e.getRawMessage());
        }
    }

    private void invokeMethod(final String method, final Object arguments) {
        registrar.activity().runOnUiThread(
                new Runnable() {
                    @Override
                    public void run() {
                        channel.invokeMethod(method, arguments);
                    }
                });
    }

    private boolean checkOss(MethodChannel.Result result) {
        if (oss == null) {
            result.error(ErrorCodes.FAILED_PRECONDITION, "not initialized", "call init first");

            return false;
        }

        return true;
    }
}
